package com.neulogistics.infrastructure.integrationeventlog.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.blackto.lib.eventbus.event.IntegrationEvent;
import com.neulogistics.infrastructure.integrationeventlog.model.EventState;
import com.neulogistics.infrastructure.integrationeventlog.model.IntegrationEventLogEntry;
import com.neulogistics.infrastructure.integrationeventlog.persistence.service.IIntegrationEventLogEntryService;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class IntegrationEventLogService implements IIntegrationEventLogService {
    private final IIntegrationEventLogEntryService integrationEventLogEntryService;

    private final List<Class<? extends IntegrationEvent>> eventType;

    private final ApplicationContext context;


    public IntegrationEventLogService(IIntegrationEventLogEntryService integrationEventLogEntryService,
                                      List<Class<? extends IntegrationEvent>> eventType,
                                      ApplicationContext context) {
        this.integrationEventLogEntryService = integrationEventLogEntryService;
        this.eventType = eventType;
        this.context = context;
    }

    @Override
    public List<IntegrationEventLogEntry> retrieveEventLogsPendingToPublish(Long transactionId) {
        LambdaQueryWrapper<IntegrationEventLogEntry> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(IntegrationEventLogEntry::getTransactionId, transactionId)
                .eq(IntegrationEventLogEntry::getEventState, EventState.NotPublished.getState());
        List<IntegrationEventLogEntry> result = integrationEventLogEntryService.list(queryWrapper);

        if(result == null){
            return new ArrayList<>();
        }
        else {
            result.forEach(p -> p.deserializeIntegrationEvent(
                    eventType.stream().filter(q -> q.getSimpleName().equals(p.getEventName())).findFirst().get()));
            return result.stream().sorted((o1, o2) -> {
                if(o1.getCreateTime().getTime() == o2.getCreateTime().getTime())
                    return 0;
                boolean before = o1.getCreateTime().before(o2.getCreateTime());
                if(before)
                    return -1;
                else
                    return 1;
            }).collect(Collectors.toList());
        }
    }

    @Override
    public void saveEvent(IntegrationEvent integrationEvent, Long transactionId) {
        IntegrationEventLogEntry eventLogEntry = new IntegrationEventLogEntry(integrationEvent, transactionId);
        integrationEventLogEntryService.save(eventLogEntry);
    }

    @Override
    public void markEventAsPublished(Long eventId) {
        updateEventStatus(eventId, EventState.Published);
    }

    @Override
    public void markEventAsInProgress(Long eventId) {
        updateEventStatus(eventId, EventState.InProcess);
    }

    @Override
    public void markEventAsFailed(Long eventId) {
        updateEventStatus(eventId, EventState.PublishedFailed);
    }

    private void updateEventStatus(Long eventId, EventState eventState){
        LambdaUpdateWrapper<IntegrationEventLogEntry> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(IntegrationEventLogEntry::getEventId, eventId)
                .set(IntegrationEventLogEntry::getEventState, eventState.getState());

        if(eventState.getState() == EventState.InProcess.getState()){
            updateWrapper.setSql("times_sent = times_sent + 1");
        }
        integrationEventLogEntryService.update(updateWrapper);
    }
}
